package while_lang.ast.stmts;

import java.util.Random;

import while_lang.ast.Exp;
import while_lang.ast.Ref;
import while_lang.ast.Stmt;
import while_lang.semantics.*;

/** Nodo del AST de While que representa una sentencia de asignación.
 * 	@author Leonardo Val
 */
public final class Assignment extends Stmt {
	public final Ref reference;
	public final Exp expression;
	
	public Assignment(Ref reference, Exp expression) {
		this.reference = reference;
		this.expression = expression;
	}
	
	@Override public String unparse() {
		return reference.unparse() +" = "+ expression.unparse() +";"; 
	}

	@Override public State evaluate(State state) {
		reference.assign(state, expression.evaluate(state));
		return state;
	}
	
	@Override public SymbolTable checkTypes(SymbolTable table) {
		/* Hay que chequea que el tipo de la expresión sea compatible con el de
		 * la referencia. Para esto se usa el método checkAssignment de la 
		 * referencia.
		 * */
		
		
		this.reference.checkAssignment(table, this.expression.checkTypes(table));
		
		return table;
	}
	
	public static Assignment generate(Random random, int min, int max) {
		return new Assignment(
			Ref.generate(random, min-1, max-1),
			Exp.generate(random, min-1, max-1)
		);
	}
	
	@Override public String toString() {
		return "Assignment(" + reference + ", "+ expression + ")";
	}

	@Override public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result
				+ ((expression == null) ? 0 : expression.hashCode());
		result = prime * result
				+ ((reference == null) ? 0 : reference.hashCode());
		return result;
	}

	@Override public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Assignment other = (Assignment) obj;
		if (expression == null) {
			if (other.expression != null)
				return false;
		} else if (!expression.equals(other.expression))
			return false;
		if (reference == null) {
			if (other.reference != null)
				return false;
		} else if (!reference.equals(other.reference))
			return false;
		return true;
	}

}
